/*
 * Copyright (C) 2006 John N. Laliberte
 *
 * This file is part of pam_grub.
 *
 * pam_grub is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * pam_grub is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with pam_grub; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include <stdio.h>
#include <string.h>
#include <unistd.h>

#define PAM_SM_PASSWORD
#include <security/pam_modules.h>
#include <security/_pam_macros.h>

#include <glib-2.0/glib.h>
#include <glib-2.0/glib/gprintf.h>

#include "utility.h"
#include "syslog.h"

typedef const void *pam_item_t;

int
pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
{
#ifdef PGDEBUG
	gchar *filename = "/home/allanonl/pam_grub/real.conf";
#else
	gchar *filename = "/boot/grub/grub.conf";
#endif
	pam_item_t item;
	int status;

	// we only want to update grub.conf if we are the root user.
	status = pam_get_item(pamh, PAM_USER, &item);
	if (status != PAM_SUCCESS)
		return status;

	// might be a better way, perhaps check uid instead of "root"
	// we could use strncmp with len here if necessary.
	if (0 != strcmp("root", item))
		return PAM_SUCCESS;

	// check to make sure the file/directory exists
	status = g_file_test(filename, G_FILE_TEST_EXISTS);
	if (! status)
		return PAM_SUCCESS;

	// retrieve the password
	status = pam_get_item(pamh, PAM_AUTHTOK, &item);
	if (status != PAM_SUCCESS)
		return status;

        // if item is blank, we didn't get the token
        if (NULL == item)
                return PAM_SUCCESS;

	// need to zero out this memory
	gchar *cleartext_pass = item;

	// find password line in grub.conf
	gchar *search_string = "password";
	int password_line_in_grub = 
		find_matching_line_in_file(filename, search_string);

	// need to figure out we want to return on failure, PAM_ABORT?
	// no password line found
	if (-1 == password_line_in_grub)
		return PAM_SUCCESS;

	// convert password to md5
        char md5_str[36];
        calculate_md5(cleartext_pass, md5_str);
	// this will free the memory and you will get
	// "No password supplied" errors by the other chained
	// PAM modules.
	// g_free(cleartext_pass);

	// create the new line we want to insert
	gchar *password_line = g_strconcat("password --md5 ", md5_str, NULL);
	
	// backup the file
	int success;
	gchar *file_contents;
	success = backup_file(filename, &file_contents);

	// it failed to backup the file.
	if (1 == success)
		return PAM_SUCCESS;

	success = replace_line(filename, &file_contents, password_line, password_line_in_grub);

	// it failed to find the line to replace
	if (1 == success)
		return PAM_SUCCESS;

	// write the new file	
	success = write_file(filename, file_contents);
	// failed to write the file.
	if (1 == success)
		return PAM_SUCCESS;

	// fix owner and file permissions on backup and regular.
	gchar *backup_name = g_strconcat(filename, ".bak", NULL);
	change_file_owner_to_root(filename);
	change_file_permissions_to_owner(filename);
        change_file_owner_to_root(backup_name);
        change_file_permissions_to_owner(backup_name);
	g_free(backup_name);


	// verify the permissions

	// LOG_ERR
	gchar *success_message="Successfully updated grub password.";
	openlog("pam_grub: ", LOG_PID, LOG_AUTHPRIV);
	syslog(LOG_INFO, "%s\n", success_message);
	g_printf("%s\n", success_message);

	return PAM_SUCCESS;
}

#ifdef PAM_STATIC
struct pam_module _pam_grub_modstruct = {
	"pam_grub",
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	pam_grub_chauthtok
};
#endif
